﻿#ifndef MKNERMIMAL_H
#define MKNERMIMAL_H

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <filesystem>
#include <chrono>
#include <thread>
#include <fstream>
#include <unordered_map>
namespace fs = std::filesystem;

#ifdef MKNERMIMAL_IMPLEMENTATION

enum class Command {
    EXIT,
    DIR,
    CD,
    PWD,
    CLS,
    ECHO,
    TYPE,
    COPY,
    DEL,
    MKDIR,
    RMDIR,
    HELP,
    COLOR,
    TITLE,
    DATE,
    VER,
    UNKNOWN
};



class MKTerminal {
private:
    HANDLE hInput;
    HANDLE hOutput;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    std::wstring currentDir;
    bool running;
    HANDLE hConsoleOutput;

    std::unordered_map<std::wstring, Command> commandMap = {
    {L"exit", Command::EXIT},
    {L"quit", Command::EXIT},
    {L"dir", Command::DIR},
    {L"ls", Command::DIR},
    {L"cd", Command::CD},
    {L"pwd", Command::PWD},
    {L"cls", Command::CLS},
    {L"clear", Command::CLS},
    {L"echo", Command::ECHO},
    {L"type", Command::TYPE},
    {L"cat", Command::TYPE},
    {L"copy", Command::COPY},
    {L"cp", Command::COPY},
    {L"del", Command::DEL},
    {L"rm", Command::DEL},
    {L"mkdir", Command::MKDIR},
    {L"rmdir", Command::RMDIR},
    {L"help", Command::HELP},
    {L"?", Command::HELP},
    {L"color", Command::COLOR},
    {L"title", Command::TITLE},
    {L"date", Command::DATE},
    {L"ver", Command::VER}
    };

    void InitializeConsole() {
        hInput = GetStdHandle(STD_INPUT_HANDLE);
        hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

        if (hInput == INVALID_HANDLE_VALUE || hOutput == INVALID_HANDLE_VALUE) {
            throw std::runtime_error("Failed to get console handle");
        }
        // set console code as UTF-8
        SetConsoleCP(65001);
        SetConsoleOutputCP(65001);

        wchar_t buffer[MAX_PATH];
        GetCurrentDirectoryW(MAX_PATH, buffer);
        currentDir = buffer;

        SetConsoleTitleW(L"MKTerminal");
        GetConsoleScreenBufferInfo(hOutput, &csbi);
    }

    void Print(const std::wstring& text) {
        DWORD written;
        WriteConsoleW(hOutput, text.c_str(), text.size(), &written, nullptr);
    }

    void ShowPrompt() {
        Print(L"\n" + currentDir + L"> ");
    }

    std::wstring ReadInput() {
        std::wstring input;
        std::getline(std::wcin, input);
        return input;
    }

    std::vector<std::wstring> ParseCommand(const std::wstring& input) {
        std::vector<std::wstring> args;
        std::wistringstream stream(input);
        std::wstring arg;
        while (stream >> arg) {
            args.push_back(arg);
        }
        return args;
    }

    void Stop() {running = false;}


    void ExecuteCommand(const std::vector<std::wstring>& args) {
        if (args.empty()) return;

        auto it = commandMap.find(args[0]);
        Command cmd = (it != commandMap.end()) ? it->second : Command::UNKNOWN;

        switch (cmd) {
        case Command::EXIT:
            running = false;
            break;
        case Command::DIR:
            ListDirectory();
            break;
        case Command::CD:
            ChangeDirectory(args);
            break;
        case Command::PWD:
            Print(currentDir + L"\n");
            break;
        case Command::CLS:
            ClearScreen();
            break;
        case Command::ECHO:
            EchoCommand(args);
            break;
        case Command::TYPE:
            TypeFile(args);
            break;
        case Command::COPY:
            CopyFile(args);
            break;
        case Command::DEL:
            DeleteFile(args);
            break;
        case Command::MKDIR:
            MakeDirectory(args);
            break;
        case Command::RMDIR:
            RemoveDirectory(args);
            break;
        case Command::HELP:
            ShowHelp();
            break;
        case Command::COLOR:
            SetColor(args);
            break;
        case Command::TITLE:
            SetTitle(args);
            break;
        case Command::DATE:
            ShowDateTime();
            break;
        case Command::VER:
            ShowVersion();
            break;
        case Command::UNKNOWN:
        default:
            Print(L"Unknown command: " + args[0] + L"\n");
            Print(L"Type 'help' to see available commands\n");
            break;
        }
    }

    void ListDirectory() {
        try {
            for (const auto& entry : fs::directory_iterator(currentDir)) {
                std::wstring name = entry.path().filename().wstring();
                std::wstring type = entry.is_directory() ? L"<DIR>" : L"     ";
                auto fileSize = entry.is_regular_file() ? entry.file_size() : 0;
                std::wstring line = std::to_wstring(fileSize);
                Print(type + L" " + line + L" " + name + L"\n");
            }
        }
        catch (const std::exception& e) {
            Print(L"Error: Unable to read directory\n");
        }
    }

    void ChangeDirectory(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: cd <directory path>\n");
            return;
        }

        std::wstring newPath = args[1];
        if (newPath == L"..") {
            fs::path parent = fs::path(currentDir).parent_path();
            if (!parent.empty()) {
                currentDir = parent.wstring();
            }
        }
        else {
            fs::path newDir = fs::path(currentDir) / newPath;
            if (fs::exists(newDir) && fs::is_directory(newDir)) {
                currentDir = fs::absolute(newDir).wstring();
            }
            else {
                Print(L"Error: Directory does not exist\n");
            }
        }
    }

    void ClearScreen() {
        COORD coord = { 0, 0 };
        DWORD written;
        FillConsoleOutputCharacterW(hOutput, L' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &written);
        SetConsoleCursorPosition(hOutput, coord);
    }

    void EchoCommand(const std::vector<std::wstring>& args) {
        std::wstring output;
        for (size_t i = 1; i < args.size(); ++i) {
            output += args[i];
            if (i < args.size() - 1) output += L" ";
        }
        output += L"\n";
        Print(output);
    }

    void TypeFile(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: type <file name>\n");
            return;
        }

        fs::path filePath = fs::path(currentDir) / args[1];
        if (!fs::exists(filePath)) {
            Print(L"Error: File does not exist\n");
            return;
        }

        try {
            std::wifstream file(filePath);
            std::wstring line;
            while (std::getline(file, line)) {
                Print(line + L"\n");
            }
        }
        catch (const std::exception& e) {
            Print(L"Error: Unable to read file\n");
        }
    }

    void CopyFile(const std::vector<std::wstring>& args) {
        if (args.size() < 3) {
            Print(L"Usage: copy <source file> <target file>\n");
            return;
        }

        fs::path src = fs::path(currentDir) / args[1];
        fs::path dst = fs::path(currentDir) / args[2];

        try {
            fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
            Print(L"File copied successfully\n");
        }
        catch (const std::exception& e) {
            Print(L"Error: File copy failed\n");
        }
    }

    void DeleteFile(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: del <file name>\n");
            return;
        }

        fs::path filePath = fs::path(currentDir) / args[1];
        try {
            if (fs::exists(filePath)) {
                fs::remove(filePath);
                Print(L"File deleted successfully\n");
            }
            else {
                Print(L"Error: File does not exist\n");
            }
        }
        catch (const std::exception& e) {
            Print(L"Error: File deletion failed\n");
        }
    }

    void MakeDirectory(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: mkdir <directory name>\n");
            return;
        }

        fs::path dirPath = fs::path(currentDir) / args[1];
        try {
            if (fs::create_directory(dirPath)) {
                Print(L"Directory created successfully\n");
            }
            else {
                Print(L"Error: Directory already exists or creation failed\n");
            }
        }
        catch (const std::exception& e) {
            Print(L"Error: Directory creation failed\n");
        }
    }

    void RemoveDirectory(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: rmdir <directory name>\n");
            return;
        }

        fs::path dirPath = fs::path(currentDir) / args[1];
        try {
            if (fs::exists(dirPath) && fs::is_directory(dirPath)) {
                fs::remove(dirPath);
                Print(L"Directory deleted successfully\n");
            }
            else {
                Print(L"Error: Directory does not exist\n");
            }
        }
        catch (const std::exception& e) {
            Print(L"Error: Directory deletion failed\n");
        }
    }

    void ShowHelp() {
        Print(L"================== MKTerminal Help =======================\n");
        Print(L"  dir/ls                 - List current directory contents  \n");
        Print(L"  cd <path>              - Change current directory         \n");
        Print(L"  pwd                    - Show current directory path      \n");
        Print(L"  cls/clear              - Clear screen                     \n");
        Print(L"  echo <text>            - Display text                     \n");
        Print(L"  type/cat <file>        - Display file contents            \n");
        Print(L"  copy/cp <src> <dst>    - Copy file                        \n");
        Print(L"  del/rm <file>          - Delete file                      \n");
        Print(L"  mkdir <dir>            - Create directory                 \n");
        Print(L"  rmdir <dir>            - Delete directory                 \n");
        Print(L"  color <fg> [bg]        - Set color (0-15)                 \n");
        Print(L"  title <text>           - Set window title                 \n");
        Print(L"  date                   - Show date and time               \n");
        Print(L"  ver                    - Show version information         \n");
        Print(L"  exit/quit              - Exit terminal                    \n");

    }

    void SetColor(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: color <foreground color> [background color]\n");
            Print(L"Color values:\n0=Black 1=Blue 2=Green 3=Aqua \n4=Red 5=Purple 6=Yellow 7=White\n");
            Print(L"8=Gray 9=Light Blue A=Light Green B=Light Aqua \n C=Light Red D=Light Purple E=Light Yellow F=Bright White\n");
            return;
        }

        int fg = std::stoi(args[1], nullptr, 16);
        int bg = args.size() > 2 ? std::stoi(args[2], nullptr, 16) : 0;

        WORD color = (bg << 4) | fg;
        SetConsoleTextAttribute(hOutput, color);
    }

    void SetTitle(const std::vector<std::wstring>& args) {
        if (args.size() < 2) {
            Print(L"Usage: title <window title>\n");
            return;
        }

        std::wstring title;
        for (size_t i = 1; i < args.size(); ++i) {
            title += args[i];
            if (i < args.size() - 1) title += L" ";
        }
        SetConsoleTitleW(title.c_str());
    }

    void ShowDateTime() {
        auto now = std::chrono::system_clock::now();
        std::time_t time = std::chrono::system_clock::to_time_t(now);
        std::wstring timeStr(20, L'\0');
        std::wcsftime(&timeStr[0], timeStr.size(), L"%Y-%m-%d %H:%M:%S", std::localtime(&time));
        Print(timeStr + L"\n");
    }

    void ShowVersion() {
        Print(L"MKTerminal v1.0\n");
        Print(L"Developed based on Windows API\n");
    }

public:
    MKTerminal() : running(true) {
        InitializeConsole();
    }

    void Run() {
        Print(L"Welcome to MKTerminal!\n");
        Print(L"Type 'help' to see available commands\n");

        while (running) {
            ShowPrompt();
            std::wstring input = ReadInput();

            if (!input.empty()) {
                auto args = ParseCommand(input);
                ExecuteCommand(args);
            }
        }

        Print(L"\nGoodbye!\n");
    }
};

#endif // MKNERMIMAL_IMPLEMENTATION

#endif // MKNERMIMAL_H